package objects;

import gfx.Tickable;
import gfx.tiles.Tile;
import gfx.tiles.TileRenderer;
import gui.Bar;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.IOException;
import java.util.ArrayList;

import sfx.SoundManager;
import sfx.SoundX;
import comm.Client;
import core.Command;
import core.Main;
import core.Vector2;

/**
 * The Robot which executes the commands of the clients in the Game.
 * 
 * @author ruben.ortlam
 */
public class Robot extends Entity implements Damageable {
	
	//mode attributes
	private Vector2 spawn;
	private int team;
	
	private Bar healthBar;
	private Vector2 newPos;
	private double turretAngle;
	private int animationState;
	
	private ArrayList<Command> commands;
	
	private int color;
	private String name;
	private int health;
	private Inventory inventory;
	
	public int wayMoved;
	
	public Client client;
	
	public SoundX engineSound = new SoundX(Main.soundManager.getSound(SoundManager.ENGINE), pos);
	public SoundX idleSound = new SoundX(Main.soundManager.getSound(SoundManager.IDLE_ENGINE), pos);
	public SoundX tankFireSound = new SoundX(Main.soundManager.getSound(SoundManager.TANKFIRE), pos);
	
	public static final Vector2 SIZE = new Vector2(40, 40);
	public static final double SPEED = 2.0;
	public static final double ANGSPEED = 0.1;
	public static final double SENSORRANGE = Math.PI / 16;
	public static final double SENSORWIDTH = 1000;
	public static final int MAX_HEALTH = 100;
	private static final int ANIMATION_DISTANCE = 3;
	private static final int FIRE_DELAY = 2000, MINE_DELAY = 10000;
	
	private long lastFire, lastMine;
	private boolean stuck;
	
	/**
	 * Creates a new Robot with the given parameters.
	 * 
	 * @param pos The position of the new Robot.
	 * @param angle The angle of the new Robot.
	 * @param color The int telling which column to take from the robot image, deciding which color the robot will have.
	 */
	public Robot(Vector2 pos, double angle, int color, String name, Client client) {
		super(pos, angle, SIZE);
		newPos = new Vector2(pos);
		this.pos = pos;
		this.angle = angle;
		this.color = color;
		this.client = client;
		this.turretAngle = 0;
		this.setName(name);
		
		/*
		inventory = new Inventory(5);
		inventory.addItem(Inventory.MINE);
		*/
		
		//mode attributes
		spawn = new Vector2(this.pos);
		team = -1;
		
		health = MAX_HEALTH;
		
		animationState = 0;
		
		commands = new ArrayList<Command>();
		
		healthBar = new Bar(new Vector2(), new Vector2(100, 10), 100);
		
		lastFire = 0l;
		lastMine = 0l;
		stuck = false;
	}
	
	@Override
	public void tick() {
		if(commands.size() > 0) {
			if(!commands.get(0).isFinished()) {
				commands.get(0).run();
			} else {
				commands.remove(0);
			}
		} else {
			if(Main.SOUND_ACTIVE && !idleSound.isPlaying()) {
//				System.out.println("NICK IST SCHULD");
				idleSound.play();
				if(client != null) {
					try {
						client.out.writeInt(1);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		
		if(health <= 0 && client != null) {
			Explosion explosion = new Explosion(new Vector2(pos), (int)SIZE.length());
			Main.addDrawable(explosion);
			Main.addTickable(explosion);
			
			pos.setPos(spawn);
			health = MAX_HEALTH;
		} else if(health <= 0 && client == null) {
			Explosion explosion = new Explosion(new Vector2(pos), (int)SIZE.length());
			Main.addDrawable(explosion);
			Main.addTickable(explosion);
			
			removed = true;
		}
		
		Damageable dmg = Main.entityCollision(this, newPos);
		
		if(Main.isInWorld(newPos) && TileRenderer.tileCollision(newPos, SIZE) == Tile.AIR && 
				dmg == null) {
			pos.setPos(newPos);
			if(stuck == true) {
				try {
					client.out.writeInt(3);
				} catch (IOException e) {
					e.printStackTrace();
				}
				stuck = false;
			}
		} else if(dmg != null) {
			damage(5);
			dmg.damage(5);
			if(dmg instanceof Robot) {
				((Robot)dmg).moveBackward();
			}
			moveBackward();
			if(stuck == true) {
				try {
					client.out.writeInt(3);
				} catch (IOException e) {
					e.printStackTrace();
				}
				stuck = false;
			}
		} else {
			if(stuck == false) {
				try {
					client.out.writeInt(2);
				} catch (IOException e) {
					e.printStackTrace();
				}
				stuck = true;
			}
			
			if(commands.size() > 0 && commands.get(0).getType() == 2) {
				commands.get(0).setFinished(true);
			}
		}
		
		if(wayMoved >= ANIMATION_DISTANCE) {
			if(animationState < 7) {
				animationState++;
			} else {
				animationState = 0;
			}
			wayMoved -= ANIMATION_DISTANCE;
		} else if(wayMoved <= -ANIMATION_DISTANCE) {
			if(animationState > 0) {
				animationState--;
			} else {
				animationState = 7;
			}
			wayMoved += ANIMATION_DISTANCE;
		}
		
		if(angle > 2 * Math.PI) {
			angle = 0;
		} else if(angle < 0) {
			angle = 2 * Math.PI;
		}
		
		if(turretAngle > 2 * Math.PI) {
			turretAngle = 0;
		} else if(turretAngle < 0) {
			turretAngle = 2 * Math.PI;
		}
		
		healthBar.setPercent((int)( (double)health / (double)MAX_HEALTH * 100D));
	}
	
	@Override
	public void render(Graphics g) {
		Graphics2D g2d = (Graphics2D)g;
		
		//Render Robot Body
		int x = (int)((pos.x - Main.mapPos.x) * Main.zoom);
		int y = (int)((pos.y - Main.mapPos.y) * Main.zoom);
		
		g2d.rotate(angle, x, y);
		g2d.drawImage(Main.tanks[color][animationState], (int)(x - SIZE.x * Main.zoom / 2), (int)(y - SIZE.x * Main.zoom / 2), (int)(SIZE.x * Main.zoom), (int)(SIZE.y * Main.zoom), null);
		
		g2d.rotate(turretAngle, x, y);
		g2d.drawImage(Main.tankGuns[color][animationState], (int)(x - SIZE.x * Main.zoom / 2), (int)(y - SIZE.x * Main.zoom / 2), (int)(SIZE.x * Main.zoom), (int)(SIZE.y * Main.zoom), null);
		g2d.rotate(-turretAngle, x, y);
		
		g2d.rotate(-angle, x, y);
		
		healthBar.setPos(new Vector2(x - SIZE.x,y - SIZE.y));
		healthBar.render(g2d);
		
		g2d.setFont(new Font("Arial", Font.BOLD, 15));
		g2d.drawString(name, x - (int)SIZE.x, y - (int)SIZE.y);
	}
	
	/**
	 * Turns the Robot left by ANGSPEED.
	 */
	public void turnLeft() {
		angle -= ANGSPEED;
	}
	/**
	 * Turns the Robot right by ANGSPEED.
	 */
	public void turnRight() {
		angle += ANGSPEED;
	}
	
	/**
	 * Turns the Turret of the Robot left by ANGSPEED.
	 */
	public void turnTurretLeft() {
		turretAngle -= ANGSPEED;
	}
	
	/**
	 * Turns the Turret of the Robot right by ANGSPEED.
	 */
	public void turnTurretRight() {
		turretAngle += ANGSPEED;
	}
	
	/**
	 * Moves the Robot forward by SPEED.
	 */
	public void moveForward() {
		wayMoved += SPEED;
		newPos.setPos(pos.x + Math.sin(angle) * SPEED, pos.y - Math.cos(angle) * SPEED);
	}
	/**
	 * Moves the Robot backward by SPEED.
	 */
	public void moveBackward() {
		wayMoved -= SPEED;
		newPos.setPos(pos.x - Math.sin(angle) * SPEED, pos.y + Math.cos(angle) * SPEED);
	}
	
	/**
	 * Damages the Robot by the given amount.
	 * 
	 * @param damage The Amount of Damage for the Robot to take.
	 */
	public void damage(int damage) {
		health -= damage;
	}
	
	/**
	 * Add the command to the robot's list of commands.
	 * 
	 * @param command The command to be added.
	 */
	public void addCommand(Command command) {
		commands.add(command);
	}
	/**
	 * Removes the command from the robot's list of commands if found.
	 * 
	 * @param commmand The command to be removed.
	 * @return true if the command was found and removed, false otherwise.
	 */
	public boolean removeCommand(Command command) {
		return commands.remove(command);
	}
	/**
	 * Clears the robot's list of commands.
	 */
	public void clearCommands() {
		commands.clear();
	}
	
	/**
	 * Returns the distance to the next robot the sensor is pointing at.
	 * 
	 * @return the distance to the next robot the sensor is pointing at, 0 if no robot is in range.
	 * @author robert.kraneis
	 * 
	 */
	public double[] getSensorDistance() {
		for (Tickable tick : Main.getTickables()) {
			if (tick.getPos() != null) {
				double temp = Math.atan2(tick.getPos().y - pos.y, tick.getPos().x - pos.x); 	// Gets the angle between the sensor and one robot.
				double angle = this.angle + this.turretAngle;
				
				while(angle > 2*Math.PI) angle -= 2*Math.PI;
				
				System.err.println(temp + " : " + angle + " : " + SENSORRANGE);
				
				if (temp < angle + SENSORRANGE && temp > angle - SENSORRANGE 					// Checks weather the angle is in range of the sensor. 
						&& tick.getPos().distance(pos) < SENSORWIDTH) {							// Checks weather the distance between sensor and robot is in range.
					return new double[] { tick.getPos().distance(pos), temp - angle };											// Returns the distance.
				}
			}
		}
		return null;
	}
	
	/**
	 * Returns the ground type in front of the robot.
	 * 
	 * @return Tile State in front of the robot
	 * 
	 */
	public int getGroundSensorData() {
		double angle = this.angle + this.turretAngle;
		
		while(angle > 2*Math.PI) angle -= 2*Math.PI;
		
		int x = (int) (pos.x + Math.sin(angle) * size.length() * 2.0 - Tile.size) / Tile.size;
        int y = (int) (pos.y - Math.cos(angle) * size.length() * 2.0 - Tile.size) / Tile.size;
        
        return TileRenderer.getTile(x, y).state();
	}
	
	public void fire() {
		if(System.currentTimeMillis() - lastFire >= FIRE_DELAY) {
			double tmp4 = angle + turretAngle;
			while(tmp4 > 2 * Math.PI) tmp4 -= 2 * Math.PI;
			
			Bullet bullet = new Bullet(new Vector2(pos.x + Math.sin(tmp4) * size.length() * 0.6, pos.y - Math.cos(tmp4) * size.length() * 0.6), tmp4, 400);
			Main.addDrawable(bullet);
			Main.addTickable(bullet);
			if(Main.SOUND_ACTIVE && !tankFireSound.isPlaying()) {
				tankFireSound.play();
			}
			
			lastFire = System.currentTimeMillis();
		}
	}
	
	public void placeMine() {
		if(Main.minesEnabled && System.currentTimeMillis() - lastMine >= MINE_DELAY) {
			Mine mine = new Mine(new Vector2(pos));
			Main.addDrawable(mine);
			Main.addTickable(mine);
			lastMine = System.currentTimeMillis();
		}
	}
	
	@Override
	public Vector2 getSize() {
		return SIZE;
	}
	
	@Override
	public Vector2 getPos() {
		return pos;
	}
	
	/**
	 * Sets the position of the robot to a new value.
	 * 
	 * @param pos The new position of the robot.
	 */
	public void setPos(Vector2 pos) {
		this.pos = pos;
	}
	
	/**
	 * Returns the angle of the robot's turret.
	 * 
	 * @return The angle of the turret.
	 */
	public double getTurretAngle() {
		return turretAngle;
	}
	
	/**
	 * Returns the angle of the robot.
	 * 
	 * @return The angle of the robot.
	 */
	public double getAngle() {
		return angle;
	}
	
	/**
	 * Returns the angle of the robot in degrees.
	 * 
	 * @return The angle of the robot in degrees.
	 */
	public double getDegreeAngle() {
		return Math.toDegrees(angle);
	}
	
	/**
	 * Set the angle to the given value.
	 * 
	 * @param angle The new angle of the robot.
	 */
	public void setAngle(double angle) {
		this.angle = angle;
	}
	
	@Override
	public boolean removed() {
		return removed;
	}
	
	@Override
	public Color getColor() {
		//System.out.println(color);
		switch (color) {
			case 0:
				return new Color(0, 155, 0);
			case 1:
				return Color.RED;
			case 2:
				return Color.YELLOW;
			case 3:
				return new Color(0xff2eccfa);
			case 4:
				return new Color(0xffc837d7);
			case 5:
				return new Color(0xff37d7d0);
			case 6:
				return Color.ORANGE;
			case 7:
				return Color.PINK;
		}
		return Color.BLACK;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setTurretAngle(double turretAngle) {
		this.turretAngle = turretAngle;
	}
	
	public void setSpawn(Vector2 spawn) {
		this.spawn = spawn;
		newPos.setPos(spawn);
	}
	
	public Vector2 getSpawn() {
		return spawn;
	}

	public int getTeam() {
		return team;
	}

	public void setTeam(int team) {
		this.team = team;
	}
	
	public Inventory getInventory() {
		return inventory;
	}
	
}
